<#-- 
    TODO _apiMetas 可以在运行时获取，请求一次Meta接口，获得业务接口列表，再加入SDK，但这会遇到运行时序的问题
 -->
/*
* BudoDubboHttpApiECMAScriptSdk
*/

"use strict";

var log = console || { warn : function () {} }; 

// 列出所有接口和接口的方法
var _apiMetas = [
<#list interfaces?sort_by("name") as interface>
    {"type": "${interface.name}","methods": [<@compress single_line=true>
        <#list interface.methods?sort_by("name") as method>
            "${method.name}"<#if method_index lt (interface.methods?size-1)>,</#if>
        </#list>
    </@compress>]}<#if interface_index lt (interfaces?size-1)>,</#if>
</#list>
]; 

// 类 BudoDubboHttpApiECMAScriptSdk
var BudoDubboHttpApiECMAScriptSdk = function() {
    var _sdk = this;

    // 属性默认值
    _sdk._url = null;

    _sdk._timeout = 25 * 1000; // 请求超时默认25秒

    _sdk._transporter = null; // 请求协议

    _sdk._responseHandler = ResponseHandler; // 结果处理

    _sdk._serialization = new JsonSerialization(); // 序列化

    // 默认的 DeferredFactory
    _sdk._deferredFactory = function(_request, _sdk) {
        return new Deferred(_request, _sdk);
    };

    _sdk._cacheManager = {
        get() {},
        put() {}
    };

    // 默认回调
    _sdk._callback = {
        "success": function() { 
            log.warn("default _sdk._callback success : arguments=" + JSON.stringify(arguments)); 
        },
        "fail": function() { 
            log.warn("default _sdk._callback fail : arguments=" + JSON.stringify(arguments)); 
        }
    };

    // 设置默认回调
    _sdk.setCallback = function(callback) {
        if ( !!!callback || !!!callback.success || !!!callback.fail || typeof callback.success != "function" || typeof callback.fail != "function" ) {
            throw "sdk.setCallback must input a object like {success:function, fail:function}";
        }

        _sdk._callback = callback;
    };

    _sdk.setCacheManager = function( cacheManager ) {
        if ( !cacheManager || !cacheManager.get || !cacheManager.put ) {
            throw "sdk.setCacheManager must input a object like {get:function, put:function}";
        }

        _sdk._cacheManager = cacheManager;
    };

    // 设置传输协议
    _sdk.setTransporter = function(transporter) {
        if (typeof transporter != "function") { // 传入function
            throw "sdk.setTransporter must input a function";
        }

        _sdk._transporter = transporter;
    };

    // 传入结果处理函数
    _sdk.setResponseHandler = function(responseHandler) {
        if (typeof responseHandler != "function") { // 传入function
            throw "sdk.setResponseHandler must input a function";
        }

        _sdk._responseHandler = responseHandler;
    };

    // 属性Setter
    _sdk.setDeferredFactory = function(deferredFactory) {
        _sdk._deferredFactory = deferredFactory;
    };

    // 属性Setter
    _sdk.setTimeout = function(timeout) {
        _sdk._timeout = timeout;
    };

    // 传入请求预处理器
    _sdk.setRequestFilter = function(requestFilter) {
        _sdk._requestFilter = requestFilter;
    };

    _sdk.setUrl = function(url) {
        _sdk._url = url;
    };

    _sdk.setSerialization = function(serialization) {
        _sdk._serialization = serialization;
    };

    // 每一个接口
    _apiMetas.forEach(function(_apiMeta) {
        _sdk[_apiMeta.type] = function() {
            var _sdk = this; // 这个 sdk 实例会带上_cached 属性
            return new InterfaceDelegate(_sdk, _apiMeta.type, _apiMeta.methods); // 每一个接口的代理
        };
    });

    // 返回带缓存逻辑的sdk
    _sdk.cache = function() {
        return Object.assign( { _cached : true }, _sdk);
    };
};
// end BudoDubboHttpApiECMAScriptSdk


// 类 InterfaceDelegate, 对应一个Java Interface
var InterfaceDelegate = function (_sdk, _interfaceName, _methodNames) {
    var _interfaceDelegate = this;

    _methodNames.forEach(function(_methodName) {
        // 逐个添加代理方法
        _interfaceDelegate[_methodName] = function() {
            var _methodArgs = Array.prototype.slice.apply(arguments);

            // 构造 _request 对象
            var _request = {
                "interfaceName": _interfaceName,
                "methodName": _methodName,
                "args": _methodArgs
            };

            return _sdk._deferredFactory(_request, _sdk); // 返回Deferred 供传入回调
        };
    });
}


// 类 JsonSerialization , 序列化
var JsonSerialization = function() {
    // obj to str
    this.serialize = function(_obj) {
        return JSON.stringify(_obj);
    };

    // str to obj
    this.deserialize = function(_str) {
        try {
            return typeof _str === "object" ? _str : JSON.parse(_str);
        } catch ( e ) {
            log.warn("#149 deserialize error, _str=" + _str + ", e=" + e, e);
            return { result : { error_description : 'deserialize error ' + _str } };
        }
    };
};

// _transporter 缓存扩展
var _cached_transporter = ( _transporter, _request ) => {
    var _sdk = _request.sdk;

    if ( !_sdk._cached ) {
         _transporter(_request);
         return;
    }

    // 成功回调
    var _success = _request.callback.success;

    var _cache_value = _sdk._cacheManager.get(_request);
    if ( !!_cache_value ) { // 缓存命中
        _request.responseBody = _cache_value; // 读缓存
        _sdk._responseHandler(_request); // 先用缓存数据 执行业务回调

        // 替换掉回调 不执行业务回调 执行写缓存
        _request.callback.success = function(_result, _req) {
            _sdk._cacheManager.put(_req); // 写缓存
        };
    } else { // 缓存未命中
        _request.callback.success = function(_result, _req) { // 替换掉回调 先执行业务回调 再写缓存
            _success(_result, _req);
            _sdk._cacheManager.put(_req); // 写缓存
        }
    }

    _transporter(_request);
};


// 类 Deferred, 请求回调
var Deferred = function (_request, _sdk) {
    var _deferred = this;

    // 传入回调，异步执行请求，支持传入两个 function 作为 两个回调 
    _deferred.then = function() {
        var _callbacks = Array.prototype.slice.apply(arguments); // then 方法参数 回调函数

        var _preparedRequest = _prepareRequest(_sdk, _request, _callbacks);

        try {
            _cached_transporter(_sdk._transporter, _preparedRequest); // 发起通信请求
        } catch(_error) {
            _request.error = _error;
            _sdk._responseHandler(_request);
        }

        return _deferred;
    }

    // 私有方法 构造 _request 对象
    var _prepareRequest = function(_sdk, _request, _callbacks) {
        var _mergedCallback = {};

        if ( !! _callbacks && !! _callbacks[0] ) {
            // 传入一个或两个function作为回调
            _mergedCallback.success = _callbacks[0]; // 第一个参数是成功回调
            _mergedCallback.fail = _callbacks[1] || _sdk._callback.fail; // 第二个参数是失败回调，没有就走默认失败回调
        } else {
            _mergedCallback = _sdk._callback;
        }

        _request.callback = _mergedCallback;

        _request.requestBody = _sdk._serialization.serialize(_request.args[0] || {}); // 无参数时传空对象

        _request.responseHandler = _sdk._responseHandler;
        _request.serialization = _sdk._serialization;
        _request.headers={};
        _request.parameters={};
        _request.timeout=_sdk._timeout;
        _request.url=_sdk._url;

        _request.sdk = _sdk;

        // 执行 requestFilter
        if (_sdk._requestFilter) {
            _request = _sdk._requestFilter(_request);
        }

        return _request;
    }
}


// ResponseHandler 返回结果处理
var ResponseHandler = function(_request) {
    if ( !!_request.error) {
        _request.callback.fail(_request.error, _request);
        return;
    }

    var _responseBody = _request.responseBody;
    var _responseEntity =  _request.serialization.deserialize(_responseBody); // 结果反序列化
    _request.callback.success(_responseEntity, _request);
};


export { BudoDubboHttpApiECMAScriptSdk } ;